﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using FitNesseRunner.Model;

namespace FitNesseRunner
{
	public class VariablesHandler
	{
		private readonly IEnumerable<string> _lines;
		private readonly IAssemblyLoader _assemblyLoader;
		private readonly IDictionary<string, string> _variables = new Dictionary<string, string>();

		public VariablesHandler(IEnumerable<string> lines, IAssemblyLoader assemblyLoader)
		{
			_lines = lines;
			_assemblyLoader = assemblyLoader;
		}

		public IEnumerable<string> GetAllLines()
		{
			foreach (var line in _lines)
			{
				const string pathPrefix = "!path ";
				if (line.StartsWith(pathPrefix))
				{
					var classPath = line.Substring(pathPrefix.Length).Trim();
					classPath = ReplaceVariables(classPath);
					_assemblyLoader.LoadAssembliesFrom(classPath);
				}
				else
				{
					if (!TryHandleVariableDefinition(line))
						yield return ReplaceVariables(line);
				}
			}
		}

		private string ReplaceVariables(string line)
		{
			const string pattern = @"\${(?<name>\w+)}";
			var regex = new Regex(pattern);
			var sb = new StringBuilder(line);
			for (var match = regex.Match(line); match.Success; match = regex.Match(sb.ToString()))
			{
				var name = GetValue(match, "name");
				sb.Replace(match.Value, _variables[name]);
			}
			return sb.ToString();
		}

		private bool TryHandleVariableDefinition(string line)
		{
			const string valueWithCurlyBraces = @"\{(?<value>[^}]*)}";
			const string valueWithParenthesis = @"\((?<value>[^)]*)\)";
			const string pattern = @"^!define (?<name>\w+)\s+((" + valueWithCurlyBraces + @")|(" + valueWithParenthesis + @"))";
			var regex = new Regex(pattern);
			var match = regex.Match(line);
			if (!match.Success)
				return false;

			var name = GetValue(match, "name");
			var value = GetValue(match, "value");
			value = ReplaceVariables(value);

			_variables[name] = value;
			return true;
		}

		private static string GetValue(Match match, string groupName)
		{
			return match.Groups[groupName].Captures.Cast<Capture>().Single().Value;
		}
	}
}